import { generateRandomString } from './utils'
import CryptoJS from 'crypto-js'
import { publicKeyPEM } from '@/constant'

const pemToBuffer = pem => {
  let encoded = ''
  pem.split('\n').forEach(line => {
    if (line.indexOf('-----') < 0) {
      encoded += line
    }
  })
  return base64ToArrayBuffer(encoded)
}
const arrayBufferToBase64 = buffer => {
  let binary = ''
  let bytes = new Uint8Array(buffer)
  let len = bytes.byteLength
  for (let i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i])
  }
  return window.btoa(binary)
}
const generateHeaders = body => {
  const timestamp = Math.floor(Date.now() / 1000).toString()
  const nonce = generateRandomString()
  const signature = generateSignature(timestamp, nonce, body)

  return {
    RegTimestamp: timestamp,
    RegNonce: nonce,
    RegSignature: signature
  }
}
const base64ToArrayBuffer = base64 => {
  let binaryString = window.atob(base64)
  let len = binaryString.length
  let bytes = new Uint8Array(len)
  for (let i = 0; i < len; i++) {
    bytes[i] = binaryString.charCodeAt(i)
  }
  return bytes.buffer
}

const generateAESKey = async (crypto, length = 256) => {
  return crypto.subtle.generateKey({ name: 'AES-GCM', length: length }, true, ['encrypt', 'decrypt'])
}

const generateSignature = (timestamp, nonce, body) => {
  const key = 'xGfLPJynSmADzImFXPZRCtenWIMvkhassPzIaNkbsgjYgThnxbebZVbXlUpPeIab'
  const rawSignature = timestamp + nonce + body
  return CryptoJS.HmacSHA256(rawSignature, key).toString(CryptoJS.enc.Hex)
}

const encryptWithAES = async (crypto, key, data) => {
  const iv = crypto.getRandomValues(new Uint8Array(12))
  const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv: iv }, key, new TextEncoder().encode(data))
  return { encryptedData: encrypted, iv: iv }
}

const rsaEncrypt = async (crypto, data) => {
  const publicKey = await crypto.subtle.importKey(
    'spki',
    pemToBuffer(publicKeyPEM),
    {
      name: 'RSA-OAEP',
      hash: 'SHA-256'
    },
    true,
    ['encrypt']
  )

  let encrypted = await crypto.subtle.encrypt(
    {
      name: 'RSA-OAEP'
    },
    publicKey,
    data
  )

  return arrayBufferToBase64(encrypted)
}

export const encryptParams = async data => {
  data = typeof data === 'string' ? data : JSON.stringify(data)
  const crypto = window.crypto || window.webkitCrypto || window.mozCrypto || window.oCrypto || window.msCrypto

  const aesKey = await generateAESKey(crypto)
  const aesEncrypted = await encryptWithAES(crypto, aesKey, data)
  const exportedAesKey = await crypto.subtle.exportKey('raw', aesKey)
  const encrypted_key = await rsaEncrypt(crypto, exportedAesKey)
  const encryption_data = arrayBufferToBase64(aesEncrypted.encryptedData)
  const iv = arrayBufferToBase64(aesEncrypted.iv)
  const body = {
    encryption_data,
    iv,
    encrypted_key
  }
  return {
    headers: generateHeaders(encryption_data),
    body
  }
}
